﻿using System;
using System.Collections;
using System.Collections.Specialized;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Net;
using System.Xml.Serialization;
using System.Xml;
using System.Web;
using Microsoft.MediaCenter;
using Microsoft.MediaCenter.UI;
using Microsoft.MediaCenter.TV.Scheduling;
using Argotic.Syndication;

namespace MCEMediaManager
{
    class HuluPlugin : MediaPlugin, IMediaPlugin
    {
        public HuluPlugin()
        {
            Microsoft.MediaCenter.UI.Application.DeferredInvoke(Login);
        }
        
        #region Private Properties
        private VirtualList _SearchResults;
        private VirtualList _DiscoveryResults;
        private Dictionary<string, VirtualList> _SearchResultsList;
        private Dictionary<string, VirtualList> _DiscoveryResultsList;
        private HuluLoginStatus _LoginStatus = HuluLoginStatus.NotLoggedIn;
        #endregion

        #region Public Properties
        public static Dictionary<string, string> HuluTypes = new Dictionary<string, string>
        {
            {"TV Episodes", "episode"},
            {"Movie Trailers", "film_trailer"},
            {"TV Clips", "clip"},
            {"Feature Films", "feature_film"},
            {"Movie Clips", "film_clip"}
        };

        public enum HuluLoginStatus { NotLoggedIn, LoggingIn, LoginFailed, LoggedIn };

        public override string Name
        {
            get
            {
                return "Hulu";
            }
        }

        public string HuluUserID
        {
            get
            {
                string value = Application.Current.DataStore.GetString("HuluUserID", "");
                return value;
            }
            set
            {
                Application.Current.DataStore.SetValue("HuluUserID", value);
            }
        }

        public string HuluPassword
        {
            get
            {
                string value = Application.Current.DataStore.GetString("HuluPassword", "");
                return value;
            }
            set
            {
                Application.Current.DataStore.SetValue("HuluPassword", value);
            }
        }

        public override bool HasSearchInterface
        {
            get { return true; }
        }

        public override bool HasDiscoveryInterface
        {
            get { return true; }
        }

        public VirtualList SearchResults
        {
            get
            {
                if (_SearchResults == null)
                {
                    _SearchResults = new VirtualList();
                }
                return (this._SearchResults);
            }
            set { this._SearchResults = value; }
        }

        public Dictionary<string, VirtualList> SearchResultsList
        {
            get
            {
                if (_SearchResultsList == null)
                {
                    _SearchResultsList = new Dictionary<string, VirtualList>();
                }
                return _SearchResultsList;
            }
            set { this._SearchResultsList = value; }
        }
        public VirtualList DiscoveryResults
        {
            get
            {
                if (_DiscoveryResults == null)
                {
                    _DiscoveryResults = new VirtualList();
                }
                return (this._DiscoveryResults);
            }
            set { this._DiscoveryResults = value; }

        }
        public Dictionary<string, VirtualList> DiscoveryResultsList
        {
            get
            {
                if (_DiscoveryResultsList == null)
                {
                    _DiscoveryResultsList = new Dictionary<string, VirtualList>();
                }
                return _DiscoveryResultsList;
            }
            set { this._DiscoveryResultsList = value; }
        }

        public override IList GetSearchResults(string filter)
        {
            return (IList) SearchResultsList[filter];
        }

        public override int SearchResultsCount
        {
            get
            {

                return _SearchResultsCount;
            }
            set
            {
                lock (this)
                {
                    if (_SearchResultsCount != value)
                    {
                        _SearchResultsCount = value;
                        Debug.WriteLine("New Search Results Count : " + value);
                        FirePropertyChanged("SearchResultsCount");
                        FirePropertyChanged("SearchLabel");
                    }
                }
            }
        }

        public override int DiscoveryItemsCount
        {
            get
            {
                return _DiscoveryItemsCount;
            }
            set
            {
                lock (this)
                {
                    if (_DiscoveryItemsCount != value)
                    {
                        _DiscoveryItemsCount = value;
                        FirePropertyChanged("DiscoveryResultsCount");
                        FirePropertyChanged("DiscoverContentLabel");
                    }
                }
            }
        }

        public override string SearchLabel
        {
            get
            {
                return this.Name + " (" + SearchResultsCount + ")";
            }
            set
            {
                base.SearchLabel = value;
            }
        }
        public override string DiscoverContentLabel
        {
            get
            {
                return this.Name + " (" + DiscoveryItemsCount + ")";
            }
            set
            {
                base.DiscoverContentLabel = value;
            }
        }
        public HuluLoginStatus LoginStatus
        {
            get
            {
                return _LoginStatus;
            }
            set
            {
                _LoginStatus = value;
            }
        }
        #endregion

        #region Methods
        public void Login(object obj)
        {
            LoginStatus = HuluLoginStatus.LoggingIn;
            CookieCollection cc = new CookieCollection();
            HttpWebRequest httpRequest = (HttpWebRequest)WebRequest.Create("https://secure.hulu.com/account/authenticate");
            httpRequest.CookieContainer = new CookieContainer();
            httpRequest.Referer = "http://hulu.com";
            httpRequest.Method = "POST";
            httpRequest.ContentType = "application/x-www-form-urlencoded";
            httpRequest.UserAgent = "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.14) Gecko/20080404 Firefox/2.0.0.14";
            httpRequest.KeepAlive = true;
            string usernamePasswordString = "login=" + HuluUserID + "&password=" + HttpUtility.UrlEncode(HuluPassword);
            byte[] data = Encoding.ASCII.GetBytes(usernamePasswordString);
            httpRequest.ContentLength = data.Length;
            Stream dataStream = null;
            try
            {
                dataStream = httpRequest.GetRequestStream();
                dataStream.Write(data, 0, data.Length);
            }
            catch (Exception ex)
            { Debug.WriteLine("Error logging in to Hulu : " + ex.Message); }
            finally
            {
                if(dataStream != null)
                    dataStream.Close();
            }
            HttpWebResponse objResponse = null;
            string result = "";
            try
            {
                objResponse = (HttpWebResponse)httpRequest.GetResponse();
                using (StreamReader sr =
                   new StreamReader(objResponse.GetResponseStream()))
                {
                    //grab the result
                    result = sr.ReadToEnd();

                    // Close and clean up the StreamReader
                    sr.Close();
                }
            }
            catch
            {
            }

            if (result == "ok=1")
            {
                //store the cookies
                //TODO : Figure out how to store the cookies and then pass them
                //          to the navigate to page method.  Don't think this is 
                //          possible though.
                //CookieCollection cookies = objResponse.Cookies;

                //String cookieString = null;
                //MemoryStream ms = new MemoryStream();
                //XmlSerializer xmlSerialize = new XmlSerializer(typeof(CookieCollection));
                //XmlTextWriter xtw = new XmlTextWriter(ms, Encoding.UTF8);

                //xmlSerialize.Serialize(xtw, cookies);

                //ms = (MemoryStream)xtw.BaseStream;
                //UTF8Encoding encoding = new UTF8Encoding();
                //cookieString = encoding.GetString(ms.ToArray());
                //ms.Close();

                //Application.Current.DataStore.SetValue("HuluCookies", cookieString);

                LoginStatus = HuluLoginStatus.LoggedIn;
            }
            else
            {
                LoginStatus = HuluLoginStatus.LoginFailed;
            }
            if(objResponse != null)
                objResponse.Close();
        }

        public HuluMediaItem ConvertRssItemToMediaItem(RssItem item)
        {
            return ConvertRssItemToMediaItem(item, "");
        }

        public HuluMediaItem ConvertRssItemToMediaItem(RssItem item, string itemType)
        {
            HuluMediaItem mi = new HuluMediaItem();
            mi.URL = item.Link;
            mi.Name = item.Title;
            Regex regexp = new Regex("<p>(?'Description'.*)</p><p>Added: (?'DateAdded'.*)<br/>Air date: (?'DateAired'.*)<br/>Duration: (?'Duration'.*)<br/>Rating: (?'Rating'[0-9.]*)", RegexOptions.IgnoreCase);
            Match match = regexp.Match(item.Description);
            mi.Description = match.Groups["Description"].ToString();
            DateTime dateAdded = new DateTime();
            DateTime.TryParse(match.Groups["DateAdded"].ToString(), out dateAdded);
            mi.DateAdded = dateAdded;
            DateTime dateAired = new DateTime();
            DateTime.TryParse(match.Groups["DateAired"].ToString(), out dateAired);
            mi.DateAired = dateAired;
            mi.Duration = match.Groups["Duration"].ToString();
            Single avgRating = 0;
            Single.TryParse(match.Groups["Rating"].ToString(), out avgRating);
            mi.AvgRating = avgRating;
            switch (itemType)
            {
                case "episode":
                    mi.Type = MediaItemType.TV;
                    break;
                case "feature_film":
                    mi.Type = MediaItemType.Movie;
                    break;
                default:
                    mi.Type = MediaItemType.Video;
                    break;
            }
            mi.Plugin = this;
            //if (item.Enclosures.Count > 0)
            //{
            //    mi.URL = new Uri("http://www.hulu.com/playerembed.swf?eid=" + item.Enclosures[0].Url.AbsolutePath.ToLower().Replace("/embed/",""));
            //}

            return mi;
        }

        public override void SearchByName(object obj)
        {
            SearchArgs args = (SearchArgs)obj;
            SearchResultsCount = 0;
            this.Filters = new List<string>();
            foreach (string itemType in HuluTypes.Keys)
            {
                RssFeed feed = RssFeed.Create(new Uri("http://www.hulu.com/feed/search?query=" + args.SearchPage.SearchText + "&sort_by=relevance&type=" + HuluTypes[itemType]));
                Microsoft.MediaCenter.UI.Application.DeferredInvoke(delegate { lock (SearchResultsList) { if (SearchResultsList.ContainsKey(itemType)) { SearchResultsList[itemType].Clear(); } } });
                foreach (RssItem item in feed.Channel.Items)
                {
                    if (item.Link.Host.ToLower().Contains("hulu.com"))
                    {
                        //AddItemToSearchList(new HuluItemArgs(itemType, item));
                        Microsoft.MediaCenter.UI.Application.DeferredInvoke(AddItemToSearchList, new HuluItemArgs(itemType, item), TimeSpan.MinValue);
                    }
                }
            }
        }
        public void AddItemToSearchList(object obj)
        {
            RssItem item = ((HuluItemArgs)obj).HuluRssItem;
            string filterName = ((HuluItemArgs)obj).FilterName;
            lock (Filters)
            {
                if (!Filters.Contains(filterName))
                {
                    Filters.Add(filterName);
                }
            }
            lock (SearchResultsList)
            {
                if(!SearchResultsList.ContainsKey(filterName))
                    SearchResultsList.Add(filterName, new VirtualList());
            }
            SearchResultsList[filterName].Add(ConvertRssItemToMediaItem(item, filterName));
            SearchResultsCount++;
        }
        public override void CreateDiscoveryItems(object obj)
        {
            while (LoginStatus == HuluLoginStatus.LoggingIn)
            {
                Thread.Sleep(2000);
            }
            this.Filters = new List<string>();
            DiscoveryItemsCount = 0;
            SearchArgs args = (SearchArgs)obj;
            if (LoginStatus == HuluLoginStatus.LoggedIn)
            {
                AddRssFeedItemsToDiscoveryResults("http://www.hulu.com/feed/queue/jasonmlambert", "Queue");
            }
            AddRssFeedItemsToDiscoveryResults("http://www.hulu.com/feed/recommendations/" + HuluUserID, "Recommendations");
            foreach (string itemType in HuluTypes.Keys)
            {
                AddRssFeedItemsToDiscoveryResults("http://www.hulu.com/feed/popular/" + HuluTypes[itemType] + "/today", itemType);
            }
        }
        public void AddRssFeedItemsToDiscoveryResults(string rssUrl, string filterName)
        {
            RssFeed feed;
            try
            {
                feed = RssFeed.Create(new Uri(rssUrl));
            }
            catch { return; }
            Microsoft.MediaCenter.UI.Application.DeferredInvoke(delegate { if (DiscoveryResultsList.ContainsKey(filterName)) { DiscoveryResultsList[filterName].Clear(); } });
            foreach (RssItem item in feed.Channel.Items)
            {
                if (item.Link.Host.ToLower().Contains("hulu.com"))
                    Microsoft.MediaCenter.UI.Application.DeferredInvoke(AddItemToDiscoveryResults, new HuluItemArgs(filterName, item), TimeSpan.MinValue);
            }
            feed = null;
        }
        public void AddItemToDiscoveryResults(object obj)
        {
            RssItem item = ((HuluItemArgs)obj).HuluRssItem;
            string filterName = ((HuluItemArgs)obj).FilterName;
            lock (Filters)
            {
                if (!Filters.Contains(filterName))
                {
                    Filters.Add(filterName);
                }
            }
            lock (DiscoveryResultsList)
            {
                if (!DiscoveryResultsList.ContainsKey(filterName))
                {
                    DiscoveryResultsList.Add(filterName, new VirtualList());
                }
            }
            DiscoveryResultsList[((HuluItemArgs)obj).FilterName].Add(ConvertRssItemToMediaItem(item));
            DiscoveryItemsCount++;
        }
        public override IList GetDiscoveryItems(string filter)
        {
            return (IList)DiscoveryResultsList[filter];
        }
        public override void OnDetailsActionInvoked(DetailsCommand command)
        {
            HuluMediaItem item = (HuluMediaItem)command.Owner;
            switch (command.Type)
            {
                case "Watch":
                    Debug.WriteLine("Navigating to page : " + item.URL.AbsoluteUri);
                    if (Application.Current.MediaCenterEnvironment != null)
                    {
                        Application.Current.MediaCenterEnvironment.NavigateToPage(PageId.ExtensibilityUrl, item.URL.AbsoluteUri);
                        Thread.Sleep(10000);
                        IntPtr winHandle = WinAPI.FindWindow("eHome Render Window", "Windows Media Center");
                        Debug.WriteLine("Window Handle : " + winHandle);
                        EnumWindows enumWin = new EnumWindows();
                        enumWin.GetWindows(winHandle);
                        foreach (EnumWindowsItem window in enumWin.Items)
                        {
                            if (window.ClassName == "MacromediaFlashPlayerActiveX")
                            {
                                window.SetFocus();
                                Debug.WriteLine("Found Flash Plugin");
                                Thread.Sleep(15000);
                                Debug.WriteLine("Sending F Key");
                                WinAPI.SendMessage(window.Handle, WinAPI.WM_KEYDOWN, (IntPtr) WinAPI.VK.KEY_F, IntPtr.Zero);
                                break;
                            }
                        }
                        //Application.Current.MessageBox("Window Handle is " + winHandle, "Window Information", 2, true);
                        //IntPtr ieHandle = Application.FindWindowEx((IntPtr)winHandle, IntPtr.Zero, "Internet Explorer_Server", IntPtr.Zero);
                        //IntPtr flashHandle = Application.FindWindowEx((IntPtr)winHandle, IntPtr.Zero, "MacromediaFlashPlayerActiveX", IntPtr.Zero);
                        //Application.Current.MessageBox("Flash Handle is " + flashHandle, "Flash Information", 2, true);
                    }
                    break;
                case "Test":
                    //int winTestHandle = Application.GetForegroundWindow();
                    //Application.Current.MessageBox("Window Handle is " + winTestHandle, "Window Information", 2, true);
                    //IntPtr ieTestHandle = Application.FindWindowEx((IntPtr)winTestHandle, IntPtr.Zero, "Internet Explorer_Server", IntPtr.Zero);
                    //IntPtr flashTestHandle = Application.FindWindowEx((IntPtr)winTestHandle, IntPtr.Zero, "MacromediaFlashPlayerActiveX", IntPtr.Zero);
                    //Application.Current.MessageBox("Flash Handle is " + flashTestHandle, "Flash Information", 2, true);
                    break;
                default:
                    base.OnDetailsActionInvoked(command);
                    break;
            }
        }
        public override List<string> InitializeFilters()
        {
            List<string> tempFilters = new List<string>();
            foreach (string filterName in HuluTypes.Keys)
            {
                tempFilters.Add(filterName);
            }
            return tempFilters;
        }
        #endregion
    }

    public class HuluItemArgs
    {
        public HuluItemArgs(string newFilterName, RssItem newHuluRssItem)
        {
            FilterName = newFilterName;
            HuluRssItem = newHuluRssItem;
        }
        public string FilterName;
        public RssItem HuluRssItem;

    }

    public class HuluMediaItem : MediaItem, IMediaItem
    {
        private DateTime _DateAdded;

        public DateTime DateAdded
        {
            get
            {
                return _DateAdded;
            }
            set
            {
                if (value != _DateAdded)
                {
                    _DateAdded = value;
                    FirePropertyChanged("DateAdded");
                }
            }
        }

        private DateTime _DateAired;

        public DateTime DateAired
        {
            get
            {
                return _DateAired;
            }
            set
            {
                if (value != _DateAired)
                {
                    _DateAired = value;
                    FirePropertyChanged("DateAired");
                }
            }
        }

        private String _Duration;

        public String Duration
        {
            get { return _Duration; }
            set
            {
                if (value != _Duration)
                {
                    _Duration = value;
                    FirePropertyChanged("Duration");
                }
            }
        }

        public override string ListDisplayProperties
        {
            get
            {
                return @"{\rtf1\ansi\ansicpg1252\deff0\deflang1033{\fonttbl{\f0\fswiss\fcharset0 Segeo;}}{\colortbl ;\red255\green255\blue255;}{\*\generator Msftedit 5.41.21.2508;}\viewkind4\uc1\pard\cf1\b\f0\fs28 Name\b0  : " + Name + @"\par\b Date Added\b0 : " + DateAdded.ToString("MMMM dd, yyyy") + @"\par\b Date Aired\b0 : " + DateAired.ToString("MMMM dd, yyyy") + @"\par \b Avg. Rating\b0 : " + AvgRating.ToString() + @" \par}";
            }
        }

        public override ArrayListDataSet Commands
        {
            get
            {
                if (_Commands == null)
                {
                    _Commands = new ArrayListDataSet(this);

                    _Commands.Add(Plugin.CreateCommand(this, "Watch", "Watch", this.Name));
                }
                return _Commands;
            }
        }

    }

}
